/*
   A class to shake the screen for fun
   Created by Randall Maas, February 22 2009
   Copyright (c) 2009, Randall Maas
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:

   Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

   Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

   Neither the name of Blackwood Designs, LLC nor the names of its contributors
   may be used to endorse or promote products derived from this software without
   specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

public partial class ScrShake : UnfocusableForm
{
   /// <summary>
   /// A timer used to drive the transition into the display
   /// </summary>
   System . Windows . Forms . Timer animTimer = new System . Windows . Forms . Timer();
   public ScrShake(double ShakeCoef, double AngleCoef) : base(true)
   {
      this.SuspendLayout();
      // 
      // Form1
      // 
      AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
      AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

      // This is needed since we're over the whole display, and we don't want the other
      // areas to be blurry
      TransparencyKey =  BackColor =ForeColor= System.Drawing.Color.Fuchsia;
      DoubleBuffered = true;
      TopMost = true;
      FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;

      ClientSize = new System.Drawing.Size(300, 300);
      Name = "topimage";
      ResumeLayout(false);

      // This is need to pass mouse clicks thru to lower layers
      ExStyle |= (int) WS . EX_TRANSPARENT;

      animTimer . Tick += delegate(object A, EventArgs E)
         {
            if (--CountDown < 1)
              Stop();

            angle += (float)((Program . Rnd . NextDouble()-0.5) * AngleCoef);
            bx = (float)((Program . Rnd . NextDouble()-0.5) * ShakeCoef);
            by = (float)((Program . Rnd . NextDouble()-0.5) * ShakeCoef);
            Invalidate();
         };

      // Sets the timer interval to 30 milli seconds.
      animTimer . Interval = 30;
   }

   int CountDown;
   float bx,by;
   Image[] screenBitmap;

   /// <summary>
   /// Rotation angle of screen
   /// </summary>
   private float angle = 0.0f;


   void Stop()
   {
      animTimer . Stop();
      Hide();
      screenBitmap = null;
   }


   internal void Screens()
   {
      if (Visible || null != screenBitmap)
        {
           return ;
        }
      // Grab the screens
      screenBitmap = Program. GrabScreens();
      CountDown = 10;

      animTimer.Start();

      angle      = 0.0f;

      // bring up the window
      Show();
      // Set size to cover all monitors
      WindowState = FormWindowState.Normal;
      Screen[] AllScreens = Screen . AllScreens;

      // full width/height of all monitors combined
      Rectangle fullSize = AllScreens[0] . Bounds;

      // find a rectangle that will encompass all monitors on the system
      // (assuming the primary monitor is on the left/top!)
      for (int i = 1; i < AllScreens . Length && i < screenBitmap . Length; i++)
      {
         Rectangle Bounds = AllScreens[i] . Bounds;

         if (Bounds . Left < fullSize . Left)
           fullSize . X = Bounds . X;

         if (Bounds . Right > fullSize . Right)
           fullSize . Width = Bounds . Right  - fullSize . X;

         if (Bounds . Top < fullSize . Top)
           fullSize . Y   = Bounds . Y;

         if (Bounds.Bottom > fullSize . Bottom)
           fullSize . Height = Bounds . Bottom - fullSize . Y;
      }


      // cover all monitors with one gigantic window
      Location = new Point(fullSize.Left, fullSize.Top);
      Size     = new Size(fullSize.Width, fullSize.Height);

      // bring it to the top
      BringToFront();
   }

   protected override void OnPaint(PaintEventArgs e)
   {
      base . OnPaint(e);
      Graphics g = e.Graphics;
      g. CompositingQuality = CompositingQuality. HighQuality;

      // for each monitor, draw the effect
      Screen[] AllScreens = Screen . AllScreens;
      for(int i = 0; i < screenBitmap . Length; i++)
      {
         if (null == screenBitmap[i])
           continue;
         g. SmoothingMode      = SmoothingMode     . HighQuality;

         // grab the size of the current monitor
         Rectangle region = AllScreens[i] . Bounds;

         double ImWidth = screenBitmap[i].Width  * e.Graphics.DpiX / screenBitmap[i] . HorizontalResolution ;
         double ImHeight= screenBitmap[i].Height * e.Graphics.DpiY / screenBitmap[i] . VerticalResolution ;
         Matrix m = new Matrix();
         m.Translate( (float)(- ImWidth  /2), 
                      (float)(- ImHeight /2), MatrixOrder . Append);

         // rotate the bitmap about the center
         m . RotateAt(angle, new Point(0,0), MatrixOrder . Append);

         // center the image no matter what its size is
         m.Translate( region . Width /2 - bx, 
                      region . Height/2 - by, MatrixOrder . Append);

         // assign our transformation matrix
         g . Transform = m;

         // draw it
         g . DrawImage(screenBitmap[i], region . Left, region . Top);
      }

   }
}
